#!/usr/bin/python
# vi: et sw=2
#
# lib_pulseaudio.py
# Copyright (C) 2009  Harry Karvonen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# Provides pulseaudio interface for python
#
# Author: Harry Karvonen <harry.karvonen@gmail.com>
#

from ctypes import *

#pulse_lib = CDLL("libpulse.so.0");
#pulse_glib_lib = CDLL("libpulse-mainloop-glib.so")
pulse_lib = CDLL("libpulse-mainloop-glib.so.0")

################################################################################
#
# Constrants
#
################################################################################

PA_CHANNELS_MAX = 32
PA_VOLUME_MUTED = 0
PA_VOLUME_NORM = 0x10000
PA_DECIBEL_MININFTY = -200.00

################################################################################
#
# Data types
#
################################################################################

pa_volume_t             = c_uint32

pa_usec_t               = c_uint64

pa_sink_flags_t         = c_uint
pa_sink_state_t         = c_int
pa_sample_format_t      = c_int
pa_channel_position_t   = c_int
pa_subscription_mask_t  = c_uint
pa_context_flags_t      = c_int
pa_subscription_event_type_t = c_int

################################################################################
#
# enums
#
################################################################################

# pa_sink_flags
PA_SINK_NOFLAGS         = 0x0000
PA_SINK_HW_VOLUME_CTRL  = 0x0001
PA_SINK_LATENCY         = 0x0002
PA_SINK_HARDWARE        = 0x0004
PA_SINK_NETWORK         = 0x0008
PA_SINK_HW_MUTE_CTRL    = 0x0010
PA_SINK_DECIBEL_VOLUME  = 0x0020
PA_SINK_FLAT_VOLUME     = 0x0040
PA_SINK_DYNAMIC_LATENCY = 0x0080

# pa_sink_state
PA_SINK_INVALID_STATE = -1,
PA_SINK_RUNNING = 0,
PA_SINK_IDLE = 1,
PA_SINK_SUSPENDED = 2,
PA_SINK_INIT = -2,
PA_SINK_UNLINKED = -3

# pa_sample_format
PA_SAMPLE_INVALID       = -1
PA_SAMPLE_U8            = 0
PA_SAMPLE_ALAW          = 1
PA_SAMPLE_ULAW          = 2
PA_SAMPLE_S16LE         = 3
PA_SAMPLE_S16BE         = 4
PA_SAMPLE_FLOAT32LE     = 5
PA_SAMPLE_FLOAT32BE     = 6
PA_SAMPLE_S32LE         = 7
PA_SAMPLE_S32BE         = 8
PA_SAMPLE_S24LE         = 9
PA_SAMPLE_S24BE         = 10
PA_SAMPLE_S24_32LE      = 11
PA_SAMPLE_S24_32BE      = 12
PA_SAMPLE_MAX           = 13

# pa_channel_position
#
# FIXME do the copy pasta
#

# pa_subscription_mask
PA_SUBSCRIPTION_MASK_NULL               = 0x0000
PA_SUBSCRIPTION_MASK_SINK               = 0x0001
PA_SUBSCRIPTION_MASK_SOURCE             = 0x0002
PA_SUBSCRIPTION_MASK_SINK_INPUT         = 0x0004
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT      = 0x0008
PA_SUBSCRIPTION_MASK_MODULE             = 0x0010
PA_SUBSCRIPTION_MASK_CLIENT             = 0x0020
PA_SUBSCRIPTION_MASK_SAMPLE_CACHE       = 0x0040
PA_SUBSCRIPTION_MASK_SERVER             = 0x0080
PA_SUBSCRIPTION_MASK_AUTOLOAD           = 0x0100
PA_SUBSCRIPTION_MASK_CARD               = 0x0200
PA_SUBSCRIPTION_MASK_ALL                = 0x02ff

# pa_context_flags
PA_CONTEXT_NOFLAGS                      = 0x0000
PA_CONTEXT_NOAUTOSPAWN                  = 0x0001
PA_CONTEXT_NOFAIL                       = 0x0002

# pa_context_state
PA_CONTEXT_UNCONNECTED                  = 0
PA_CONTEXT_CONNECTING                   = 1
PA_CONTEXT_AUTHORIZING                  = 2
PA_CONTEXT_SETTING_NAME                 = 3
PA_CONTEXT_READY                        = 4
PA_CONTEXT_FAILED                       = 5
PA_CONTEXT_TERMINATED                   = 6

# pa_subscription_event_type
PA_SUBSCRIPTION_EVENT_SINK              = 0x0000
PA_SUBSCRIPTION_EVENT_SOURCE            = 0x0001
PA_SUBSCRIPTION_EVENT_SINK_INPUT        = 0x0002
PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT     = 0x0003
PA_SUBSCRIPTION_EVENT_MODULE            = 0x0004
PA_SUBSCRIPTION_EVENT_CLIENT            = 0x0005
PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE      = 0x0006
PA_SUBSCRIPTION_EVENT_SERVER            = 0x0007
PA_SUBSCRIPTION_EVENT_AUTOLOAD          = 0x0008
PA_SUBSCRIPTION_EVENT_CARD              = 0x0009
PA_SUBSCRIPTION_EVENT_FACILITY_MASK     = 0x000F
PA_SUBSCRIPTION_EVENT_NEW               = 0x0000
PA_SUBSCRIPTION_EVENT_CHANGE            = 0x0010
PA_SUBSCRIPTION_EVENT_REMOVE            = 0x0020
PA_SUBSCRIPTION_EVENT_TYPE_MASK         = 0x0030

################################################################################
#
# Structs
#
################################################################################

class PA_IO_EVENT(Structure):
  _fields_ = [("_opaque_struct", c_int)]

class PA_MAINLOOP(Structure):
  _fields_ = [("_opaque_struct", c_int)]

class PA_GLIB_MAINLOOP(Structure):
  _fields_ = [("_opaque_struct", c_int)]

class PA_MAINLOOP_API(Structure):
  _fields_ = [("_opaque_struct", c_int)]

class PA_CONTEXT(Structure):
  _fields_ = [("_opaque_struct", c_int)]

class PA_OPERATION(Structure):
  _fields_ = [("_opaque_struct", c_int)]

# Check
class PA_PROPLIST(Structure):
  _fields_ = [("_opaque_struct", c_int)]

# Check
class PA_SAMPLE_SPEC(Structure):
  _fields_ = [
    ("format",          pa_sample_format_t),
    ("rate",            c_uint32),
    ("channels",        c_uint8)
  ]

# Check
class PA_CHANNEL_MAP(Structure):
  _fields_ = [
    ("channels",        c_uint8),
    ("map",             pa_channel_position_t * PA_CHANNELS_MAX)
  ]

# Check
class PA_CVOLUME(Structure):
  _fields_ = [
    ("channels",        c_uint8),
    ("values",          pa_volume_t * PA_CHANNELS_MAX)
  ]

# Check
class PA_SINK_INPUT_INFO(Structure):
  _fields_ = [
    ("index",           c_uint32),
    ("name",            c_char_p),
    ("owner_module",    c_uint32),
    ("client",          c_uint32),
    ("sink",            c_uint32),
    ("sample_spec",     PA_SAMPLE_SPEC),
    ("channel_map",     PA_CHANNEL_MAP),
    ("volume",          PA_CVOLUME),
    ("buffer_usec",     pa_usec_t),
    ("sink_usec",       pa_usec_t),
    ("resample_method", c_char_p),
    ("driver",          c_char_p),
    ("mute",            c_int),
    ("proplist",        POINTER(PA_PROPLIST))
  ]

# Check
class PA_SINK_INFO(Structure):
  _fields_ = [
    ("name",                c_char_p),
    ("index",               c_uint32),
    ("description",         c_char_p),
    ("sample_spec",         PA_SAMPLE_SPEC),
    ("channel_map",         PA_CHANNEL_MAP),
    ("owner_module",        c_uint32),
    ("volume",              PA_CVOLUME),
    ("mute",                c_int),
    ("monitor_source",      c_uint32),
    ("monitor_source_name", c_char_p),
    ("latency",             pa_usec_t),
    ("driver",              c_char_p),
    ("flags",               pa_sink_flags_t),
    ("proplist",            POINTER(PA_PROPLIST)),
    ("configured_latency",  pa_usec_t),
    ("base_volume",         pa_volume_t),
    ("state",               pa_sink_state_t),
    ("n_volume_steps",      c_uint32),
    ("card",                c_uint32),
    ("n_ports",             c_uint32),
    ("ports",               POINTER(POINTER(c_int))), # FIXME
    ("active_port",         POINTER(c_int)) # FIXME
  ]

# Check
class PA_CLIENT_INFO(Structure):
  _fields_ = [
    ("index",        c_uint32),
    ("name",         c_char_p),
    ("owner_module", c_uint32),
    ("driver",       c_char_p),
    ("proplist",    POINTER(PA_PROPLIST))
  ]

#class PA_SOURCE_OUTPUT_INFO(Structure):
#  _fields_ = [
#    ("index",           c_uint32),
#    ("name",            c_char_p),
#    ("owner_module",    c_uint32),
#    ("client",          c_uint32),
#    ("source",            c_uint32),
#    ("sample_spec",     PA_SAMPLE_SPEC),
#    ("channel_map",     PA_CHANNEL_MAP),
#    ("volume",          PA_CVOLUME),
#    ("buffer_usec",     pa_usec_t),
#    ("sink_usec",       pa_usec_t),
#    ("resample_method", c_char_p),
#    ("driver",          c_char_p),
#    ("mute",            c_int)
#    #("proplist",        POINTER(c_int))
#  ]
#
#class PA_SOURCE_INFO(Structure):
#  _fields_ = [
#    ("name",                 c_char_p),
#    ("index",                c_uint32),
#    ("description",          c_char_p),
#    ("sample_spec",          PA_SAMPLE_SPEC),
#    ("channel_map",          PA_CHANNEL_MAP),
#    ("owner_module",         c_uint32),
#    ("volume",               PA_CVOLUME),
#    ("mute",                 c_int),
#    ("monitor_of_sink",      c_uint32),
#    ("monitor_of_sink_name", c_char_p),
#    ("latency",              pa_usec_t),
#    ("driver",               c_char_p),
#    ("flags",                c_int),
#    ("proplist",             POINTER(c_int)),
#    ("configured_latency",   pa_usec_t)
#  ]

################################################################################
#
# Callback types
#
################################################################################

# SIGNAL
PA_SIGNAL_CB_T = CFUNCTYPE(c_void_p,
                           POINTER(PA_MAINLOOP_API),
                           POINTER(c_int), # FIXME wrong type
                           c_int,
                           c_void_p)

# STATE
PA_STATE_NOTIFY_CB_T = CFUNCTYPE(c_void_p,
                                 POINTER(PA_CONTEXT),
                                 c_void_p)

# CLIENT
PA_CLIENT_INFO_CB_T = CFUNCTYPE(c_void_p,
                                POINTER(PA_CONTEXT),
                                POINTER(PA_CLIENT_INFO),
                                c_int,
                                c_void_p)
# SINK INPUT
PA_SINK_INPUT_INFO_CB_T = CFUNCTYPE(c_void_p,
                                    POINTER(PA_CONTEXT),
                                    POINTER(PA_SINK_INPUT_INFO),
                                    c_int,
                                    c_void_p)
                
# SINK
PA_SINK_INFO_CB_T = CFUNCTYPE(c_void_p,
                              POINTER(PA_CONTEXT),
                              POINTER(PA_SINK_INFO),
                              c_int,
                              c_void_p)
# CONTEXT
PA_CONTEXT_DRAIN_CB_T = CFUNCTYPE(c_void_p,
                                  POINTER(PA_CONTEXT),
                                  c_void_p)

PA_CONTEXT_SUCCESS_CB_T = CFUNCTYPE(c_void_p,
                                    POINTER(PA_CONTEXT),
                                    c_int,
                                    c_void_p)

PA_CONTEXT_SUBSCRIBE_CB_T = CFUNCTYPE(
        c_void_p,
        POINTER(PA_CONTEXT),
        pa_subscription_event_type_t,
        c_uint32,
        c_void_p
)
################################################################################
#
# Functions
#
################################################################################

#
# pa_strerror
pa_strerror = pulse_lib.pa_strerror
pa_strerror.restype = c_char_p
pa_strerror.argtypes = [
        c_int
]

#
# pa_mainloop_*
# Check
pa_mainloop_new = pulse_lib.pa_mainloop_new
pa_mainloop_new.restype = POINTER(PA_MAINLOOP)
pa_mainloop_new.argtypes = [ ]

pa_glib_mainloop_new = pulse_lib.pa_glib_mainloop_new
pa_glib_mainloop_new.restype = POINTER(PA_GLIB_MAINLOOP)
pa_glib_mainloop_new.argtypes = [
        POINTER(c_int) # FIXME POINTER(GMainContext)
]

# Check
pa_mainloop_get_api = pulse_lib.pa_mainloop_get_api
pa_mainloop_get_api.restype = POINTER(PA_MAINLOOP_API)
pa_mainloop_get_api.argtypes = [
        POINTER(PA_MAINLOOP)
]

pa_glib_mainloop_get_api = pulse_lib.pa_glib_mainloop_get_api
pa_glib_mainloop_get_api.restype = POINTER(PA_MAINLOOP_API)
pa_glib_mainloop_get_api.argtypes = [
        POINTER(PA_GLIB_MAINLOOP)
]

pa_mainloop_run = pulse_lib.pa_mainloop_run
pa_mainloop_run.restype = c_int
pa_mainloop_run.argtypes = [
        POINTER(PA_MAINLOOP),
        POINTER(c_int)
]

pa_mainloop_iterate = pulse_lib.pa_mainloop_iterate
pa_mainloop_iterate.restype = c_int
pa_mainloop_iterate.argtypes = [
        POINTER(PA_MAINLOOP),
        c_int,
        POINTER(c_int)
]

pa_mainloop_quit = pulse_lib.pa_mainloop_quit
#pa_mainloop_quit.restype = c_int
pa_mainloop_quit.argtypes = [
        POINTER(PA_MAINLOOP),
        POINTER(c_int)
]

pa_mainloop_dispatch = pulse_lib.pa_mainloop_dispatch
pa_mainloop_dispatch.restype = c_int
pa_mainloop_dispatch.argtypes = [
        POINTER(PA_MAINLOOP)
]

pa_mainloop_free = pulse_lib.pa_mainloop_free
pa_mainloop_free.restype = c_int
pa_mainloop_free.argtypes = [
        POINTER(PA_MAINLOOP)
]

pa_glib_mainloop_free = pulse_lib.pa_glib_mainloop_free
pa_glib_mainloop_free.restype = c_int
pa_glib_mainloop_free.argtypes = [
        POINTER(PA_GLIB_MAINLOOP)
]

#
# pa_signal_*
pa_signal_init = pulse_lib.pa_signal_init
pa_signal_init.restype = c_int
pa_signal_init.argtypes = [
        POINTER(PA_MAINLOOP_API)
]

pa_signal_new = pulse_lib.pa_signal_new
pa_signal_new.restype = None #POINTER(c_int) #FIXME PA_SIGNAL_EVENT)
pa_signal_new.argtypes = [
        c_int,
        PA_SIGNAL_CB_T,
        POINTER(c_int)
]

#
# pa_context_*
pa_context_errno = pulse_lib.pa_context_errno
pa_context_errno.restype = c_int
pa_context_errno.argtypes = [
        POINTER(PA_CONTEXT)
]

pa_context_new = pulse_lib.pa_context_new
pa_context_new.restype = POINTER(PA_CONTEXT)
pa_context_new.argtypes = [
        POINTER(PA_MAINLOOP_API),
        c_char_p
]

pa_context_new_with_proplist = pulse_lib.pa_context_new_with_proplist
pa_context_new_with_proplist.restype = POINTER(PA_CONTEXT)
pa_context_new_with_proplist.argtypes = [
        POINTER(PA_MAINLOOP_API),
        c_char_p,
        POINTER(PA_PROPLIST)
]

pa_context_set_state_callback = pulse_lib.pa_context_set_state_callback
pa_context_set_state_callback.restype = None
pa_context_set_state_callback.argtypes = [
        POINTER(PA_CONTEXT),
        PA_STATE_NOTIFY_CB_T,
        c_void_p
]

pa_context_set_subscribe_callback = pulse_lib.pa_context_set_subscribe_callback
pa_context_set_subscribe_callback.restype = None
pa_context_set_subscribe_callback.argtypes = [
        POINTER(PA_CONTEXT),
        PA_CONTEXT_SUBSCRIBE_CB_T,
        c_void_p
]

pa_context_connect = pulse_lib.pa_context_connect
pa_context_connect.restype = c_int
pa_context_connect.argtypes = [
        POINTER(PA_CONTEXT),
        c_char_p,
        pa_context_flags_t,
        POINTER(c_int) # FIXME POINTER(pa_spawn_api)
]

pa_context_get_state = pulse_lib.pa_context_get_state
pa_context_get_state.restype = c_int;
pa_context_get_state.argtypes = [
        POINTER(PA_CONTEXT)
]

pa_context_get_protocol_version = pulse_lib.pa_context_get_protocol_version
pa_context_get_protocol_version.restype = c_uint32
pa_context_get_protocol_version.argtypes = [
        POINTER(PA_CONTEXT)
]

pa_context_get_server = pulse_lib.pa_context_get_server 
pa_context_get_server.restype = c_char_p
pa_context_get_server.argtypes = [
        POINTER(PA_CONTEXT)
]

pa_context_drain = pulse_lib.pa_context_drain
pa_context_drain.restype = POINTER(PA_OPERATION)
pa_context_drain.argtypes = [
        POINTER(PA_CONTEXT),
        PA_CONTEXT_DRAIN_CB_T,
        c_void_p
]

pa_context_disconnect = pulse_lib.pa_context_disconnect
pa_context_disconnect.restype = c_int;
pa_context_disconnect.argtypes = [
        POINTER(PA_CONTEXT)
]

pa_context_subscribe = pulse_lib.pa_context_subscribe
pa_context_subscribe.retype = POINTER(PA_OPERATION)
pa_context_subscribe.argtypes = [
        POINTER(PA_CONTEXT),
        pa_subscription_mask_t,
        PA_CONTEXT_SUCCESS_CB_T,
        c_void_p
]

#
# pa_context_*_sink_*
pa_context_get_sink_input_info_list = pulse_lib.pa_context_get_sink_input_info_list
pa_context_get_sink_input_info_list.restype = POINTER(c_int)
pa_context_get_sink_input_info_list.argtypes = [
        POINTER(PA_CONTEXT),
        PA_SINK_INPUT_INFO_CB_T,
        c_void_p
]

pa_context_get_sink_info_list = pulse_lib.pa_context_get_sink_info_list
pa_context_get_sink_info_list.restype = POINTER(c_int)
pa_context_get_sink_info_list.argtypes = [
        POINTER(PA_CONTEXT),
        PA_SINK_INFO_CB_T,
        c_void_p
]

pa_context_set_sink_mute_by_index = pulse_lib.pa_context_set_sink_mute_by_index
pa_context_set_sink_mute_by_index.restype = POINTER(c_int)
pa_context_set_sink_mute_by_index.argtypes = [
        POINTER(PA_CONTEXT),
        c_uint32,
        c_int,
        PA_CONTEXT_SUCCESS_CB_T,
        c_void_p
]

pa_context_set_sink_input_mute = pulse_lib.pa_context_set_sink_input_mute
pa_context_set_sink_input_mute.restype = POINTER(c_int)
pa_context_set_sink_input_mute.argtypes = [
        POINTER(PA_CONTEXT),
        c_uint32,
        c_int,
        PA_CONTEXT_SUCCESS_CB_T,
        c_void_p
]

pa_context_set_sink_volume_by_index = pulse_lib.pa_context_set_sink_volume_by_index
pa_context_set_sink_volume_by_index.restype = POINTER(PA_OPERATION)
pa_context_set_sink_volume_by_index.argtypes = [
        POINTER(PA_CONTEXT),
        c_uint32,
        POINTER(PA_CVOLUME),
        PA_CONTEXT_SUCCESS_CB_T,
        c_void_p
]

pa_context_set_sink_input_volume = pulse_lib.pa_context_set_sink_input_volume
pa_context_set_sink_input_volume.restype = POINTER(PA_OPERATION)
pa_context_set_sink_input_volume.argtypes = [
        POINTER(PA_CONTEXT),
        c_uint32,
        POINTER(PA_CVOLUME),
        PA_CONTEXT_SUCCESS_CB_T,
        c_void_p
]

#
# pa_context_*_client_*

pa_context_get_client_info_list = pulse_lib.pa_context_get_client_info_list
pa_context_get_client_info_list.restype = POINTER(c_int)
pa_context_get_client_info_list.argtypes = [
        POINTER(PA_CONTEXT),
        PA_CLIENT_INFO_CB_T,
        c_void_p
]
pa_context_get_client_info = pulse_lib.pa_context_get_client_info
pa_context_get_client_info.restype = POINTER(c_int)
pa_context_get_client_info.argtypes = [
        POINTER(PA_CONTEXT),
        c_uint32,
        PA_CLIENT_INFO_CB_T,
        c_void_p
]

#
# pa_operation_*
pa_operation_unref = pulse_lib.pa_operation_unref
pa_operation_unref.restype = None
pa_operation_unref.argtypes = [
        POINTER(PA_OPERATION)
]

#
# pa_proplist_*
pa_proplist_new = pulse_lib.pa_proplist_new
pa_proplist_new.restype = POINTER(PA_PROPLIST)
pa_proplist_new.argtypes = [ ]

pa_proplist_sets = pulse_lib.pa_proplist_sets
pa_proplist_sets.restype = c_int
pa_proplist_sets.argtypes = [
        POINTER(PA_PROPLIST),
        c_char_p,
        c_char_p
]

#
# pa_cvolume_*

pa_cvolume_avg = pulse_lib.pa_cvolume_avg
pa_cvolume_avg.restype = pa_volume_t
pa_cvolume_avg.argtypes = [
        POINTER(PA_CVOLUME)
]

pa_cvolume_max = pulse_lib.pa_cvolume_max
pa_cvolume_max.restype = pa_volume_t
pa_cvolume_max.argtypes = [
        POINTER(PA_CVOLUME),
]

#
# pa_sw_volume_*

pa_sw_volume_to_linear = pulse_lib.pa_sw_volume_to_linear
pa_sw_volume_to_linear.restype = c_double
pa_sw_volume_to_linear.argtypes = [
        pa_volume_t
]

pa_sw_volume_from_linear = pulse_lib.pa_sw_volume_from_linear
pa_sw_volume_from_linear.restype = pa_volume_t
pa_sw_volume_from_linear.argtypes = [
        c_double
]

pa_sw_volume_to_dB = pulse_lib.pa_sw_volume_to_dB
pa_sw_volume_to_dB.restypes = c_double
pa_sw_volume_to_dB.argtypes = [
        pa_volume_t
]

pa_cvolume_scale = pulse_lib.pa_cvolume_scale
pa_cvolume_scale.restype = POINTER(PA_CVOLUME)
pa_cvolume_scale.argtypes = [
        POINTER(PA_CVOLUME),
        pa_volume_t
]
